/*
 * pedidos.c
 *
 *  Created on: 26/10/2011
 *      Author: MadeYMaga
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <semaphore.h>
#include <errno.h>
#include "pedidos.h"
#include "busqueda.h"
#include "Disco.h"
#include "nipc.h"
#include "fs.h"

extern struct t_pedido 	*lista1, *lista2;
extern int 				cantidadDePedidos1, cantidadDePedidos2;
extern int 				algoritmo, sentido, pistaActual, sectorActual, modoinit, discoid, sectors;
//extern t_log 			*s_log;
extern sem_t			semPedidos;

int 					lista1Ocupada = 0;
struct t_pedido			*ultimoPedido = NULL;
pthread_mutex_t 		mutexListaPedidos;

void *hiloPedidos(){

	struct t_pedido 	*pedido, *pedAnterior = NULL;

	pthread_mutex_init(&mutexListaPedidos, NULL);

	switch(algoritmo)
	{
		case FSCAN:
			while(1)
			{
				sem_wait(&semPedidos);

				if (lista1Ocupada)/* La lista 1 tiene pedidos? Es decir esta ocupada y tengo que atender los pedidos */
				{
					if(cantidadDePedidos1 != 0)
					{
						reordenarLista(&lista1, cantidadDePedidos1);
						recorrerLista(&lista1, &cantidadDePedidos1);
					}

					lista1Ocupada = 0;
				}
				else
				{
				if(cantidadDePedidos2 != 0)
				{
					reordenarLista(&lista2, cantidadDePedidos2);
					recorrerLista(&lista2, &cantidadDePedidos2);
				}
					lista1Ocupada = 1;
				}
			}
		break;
		case SSTF:
			while(1)
			{
				sem_wait(&semPedidos);

				pedido = SSTF_proximoPedido(&lista1, &pedAnterior);

				atenderPedido(pedido, pedAnterior, cantidadDePedidos1);

				pthread_mutex_lock(&mutexListaPedidos);
				sacarPedidoSSTF(&lista1, pedAnterior);
				pthread_mutex_unlock(&mutexListaPedidos);
			}
		break;
	}

	sem_destroy(&semPedidos);

	pthread_mutex_destroy(&mutexListaPedidos);

	pthread_exit(NULL);

	return NULL;
}

void reordenarLista(struct t_pedido **lista, int longitud){

	struct t_pedido	*aux, *ant, *auxPtr, *ptrEnganche, *newLista;

	aux = *lista;
	ant = *lista;

	if(longitud != 1)
	{
		switch(sentido)
		{
		case SUBE:
			/* si tengo que cambiar el orden de la lista */
			if((*lista)->pista < pistaActual)
			{
				/* busco pedido con pista >= a la pista actual */
				while((aux != NULL) && (aux->pista < pistaActual))
				{
					ant = aux;
					aux = aux->siguiente;
				}

				newLista = *lista;
				*lista = (*lista)->siguiente;
				newLista->siguiente = NULL;
				ptrEnganche = ultimoPedido;
				ultimoPedido = newLista;

				/* si no tengo ningun pedido mayor a la pista actual, tengo que cambiar de sentido
				 * por ende cambio el orden de la lista */
				if(aux == NULL)
				{
					while(lista != NULL)
					{
						aux = newLista;
						newLista = *lista;
						*lista = (*lista)->siguiente;
						newLista->siguiente = aux;
					}

					*lista = newLista;

				} else {

					/* hago el enganche del ultimo pedido con los primeros pedidos en el orden correspondiente */
					while(*lista != aux)
					{
						auxPtr = newLista;
						newLista = *lista;
						*lista = (*lista)->siguiente;
						newLista->siguiente = auxPtr;
					}

					ptrEnganche->siguiente = newLista;
				}
			}
			break;

		case BAJA:
			/* si tengo que cambiar el orden de la lista */
			if((*lista)->pista < pistaActual)
			{
				/* busco pedido con pista > a la pista actual */
				while((aux != NULL) && (aux->pista <= pistaActual))
				{
					ant = aux;
					aux = aux->siguiente;
				}

				newLista = *lista;
				*lista = (*lista)->siguiente;
				newLista->siguiente = aux;

				/* hago el enganche del ultimo pedido con los primeros pedidos en el orden correspondiente */
				while(*lista != aux)
				{
					auxPtr = newLista;
					newLista = *lista;
					*lista = (*lista)->siguiente;
					newLista->siguiente = auxPtr;
				}

				*lista = newLista;
			}
			break;
		}
	}
}

void recorrerLista(struct t_pedido **lista, int *longitud)
{
	int cantPedidos = 0, i = 0;

	cantPedidos = *longitud;

	for(i=0; i < cantPedidos; i++)
	{
		atenderPedido(*lista, (*lista)->siguiente, *longitud);
		liberarPedido(lista);
		(*longitud)--;
	}
}

/* TODO tooooodo el tema de vector a lista */
void atenderPedido(struct t_pedido *pedido, struct t_pedido *siguientePedido, int tamaniolista){

	nipcMessage 	msg;
	int 			tiempoTrans;
	char 			*payload = NULL, *secRecorridos, *pedidosRecorridos;

	switch (pedido->clase)
	{
		case 1: /* LEER */
		if(leerSector(pedido->sector, &(pedido->buffer)))
		{
			printf("No se pudo leer el sector.");
			strcpy(pedido->buffer,"No se pudo leer el sector");
			armarPayload(*pedido, &payload);

			sprintf(msg.payloadID,"%d", pedido->socket_id);
			msg.type = '-';
			msg.payloadLength = strlen(payload) + 1;
			msg.payload = payload;
			nipc_send(&msg, pedido->socket_id);
		} else {
/*			switch(pedido.id_proceso)
			{
			case P_RAID:
				log_info(s_log,"Thread-Raid", "Message info: %s.", "se atendio un pedido de lectura.");

				pedidosRecorridos = malloc(sizeof(char *));
				recorrerPendientes(lista, tamaniolista, &pedidosRecorridos);

				log_info(s_log,"Thread-Raid", "Cola de Pedidos: [%s]", pedidosRecorridos);

				free(pedidosRecorridos);

				log_info(s_log,"Thread-Raid", "Tamanio: %d.", tamaniolista);
				log_info(s_log,"Thread-Raid", "Posicion actual: %d:%d.", pistaActual, sectorActual);
				log_info(s_log,"Thread-Raid", "Sector solicitado: %d:%d.", pedido.pista, pedido.sector);

				secRecorridos = malloc(sizeof(char *));
				recorrerSectores(pedido, &secRecorridos);

				log_info(s_log,"Thread-Raid", "Sectores recorridos: %s.", secRecorridos);

				free(secRecorridos);

				tiempoTrans = timeTrans(pedido.sector, pedido.pista, LEER);
				log_info(s_log,"Thread-Raid", "Tiempo consumido: %s ms.", tiempoTrans);
				log_info(s_log,"Thread-Raid", "Proximo sector: %d:%d.", siguientePedido.pista, siguientePedido.sector);
				break;
			case P_CONSOLA:
				break;
			case P_FS:
				log_info(s_log,"Thread-FileSystem", "Message info: %s","se realizo un pedido de escritura.");

				pedidosRecorridos = malloc(sizeof(char *));
				recorrerPendientes(lista, tamaniolista, &pedidosRecorridos);

				log_info(s_log,"Thread-FileSystem", "Cola de Pedidos: [%s]", pedidosRecorridos);

				free(pedidosRecorridos);

				log_info(s_log,"Thread-FileSystem", "Tamanio: %d.", tamaniolista);
				log_info(s_log,"Thread-FileSystem", "Posicion actual: %d:%d.", pistaActual, sectorActual);
				log_info(s_log,"Thread-FileSystem", "Sector solicitado: %d:%d.", pedido.pista, pedido.sector);

				secRecorridos = malloc(sizeof(char *));
				recorrerSectores(pedido, &secRecorridos);

				log_info(s_log,"Thread-FileSystem", "Sectores recorridos: %s.", secRecorridos);

				free(secRecorridos);
				tiempoTrans = timeTrans(pedido.sector, pedido.pista, LEER);

				log_info(s_log,"Thread-FileSystem", "Tiempo consumido: %s ms.", tiempoTrans);
				log_info(s_log,"Thread-FileSystem", "Proximo sector: %d:%d.", siguientePedido.pista, siguientePedido.sector);

				break;
			case 	P_FS_LISTENER:
				break;
			}
*/
			printf("Se leyo el sector exitosamente.\n");
			armarPayload(*pedido, &payload);

			sprintf(msg.payloadID,"%d", pedido->socket_id);
			msg.type = LEER_C;
			msg.payloadLength = strlen(payload) + 1;
			memcpy(msg.payload, payload, strlen(payload));
			nipc_send(&msg, pedido->socket_id);
		}
		break;

		case 2: /* ESCRIBIR */
		if(escribirSector(pedido->sector, &(pedido->buffer)))
		{
			printf("Error al escribir el sector.\n");
			strcpy(pedido->buffer, "Error al escribir el sector.");
			armarPayload(*pedido, &payload);

			sprintf(msg.payloadID,"%d", pedido->socket_id);
			msg.type = '-';
			msg.payloadLength = strlen(payload) + 1;
			msg.payload = payload;
			nipc_send(&msg, pedido->socket_id);
		} else {
/*			switch(pedido.id_proceso)
			{
		case P_RAID:
				log_info(s_log,"Thread-Raid", "Message info: %s.", "se atendio un pedido de escritura.");

				pedidosRecorridos = malloc(sizeof(char *));
				recorrerPendientes(lista, tamaniolista, &pedidosRecorridos);

				log_info(s_log,"Thread-Raid", "Cola de Pedidos: [%s]", &pedidosRecorridos);

				free(pedidosRecorridos);

				log_info(s_log,"Thread-Raid", "Tamanio: %d.", tamaniolista);
				log_info(s_log,"Thread-Raid", "Posicion actual: %d:%d.", pistaActual, sectorActual);
				log_info(s_log,"Thread-Raid", "Sector solicitado: %d:%d.", pedido.pista, pedido.sector);

				secRecorridos = malloc(sizeof(char *));
				recorrerSectores(pedido, &secRecorridos);

				log_info(s_log,"Thread-Raid", "Sectores recorridos: %s.", secRecorridos);

				free(secRecorridos);

				tiempoTrans = timeTrans(pedido.sector, pedido.pista, LEER);
				log_info(s_log,"Thread-Raid", "Tiempo consumido: %s ms.", tiempoTrans);
				log_info(s_log,"Thread-Raid", "Proximo sector: %d:%d.", siguientePedido.pista, siguientePedido.sector);
				break;
			case P_CONSOLA:
				break;
			case P_FS_LISTENER:
				break;
			case P_FS:
				log_info(s_log,"Thread-FileSystem", "Message info: %s","se realizo un pedido de escritura.");

				pedidosRecorridos = malloc(sizeof(char *));
				recorrerPendientes(lista, tamaniolista, &pedidosRecorridos);

				log_info(s_log,"Thread-FileSystem", "Cola de Pedidos: [%s]", pedidosRecorridos);

				free(pedidosRecorridos);

				log_info(s_log,"Thread-FileSystem", "Tamanio: %d.", tamaniolista);
				log_info(s_log,"Thread-FileSystem", "Posicion actual: %d:%d.", pistaActual, sectorActual);
				log_info(s_log,"Thread-FileSystem", "Sector solicitado: %d:%d.", pedido.pista, pedido.sector);

				secRecorridos = malloc(sizeof(char *));
				recorrerSectores(pedido, &secRecorridos);

				log_info(s_log,"Thread-FileSystem", "Sectores recorridos: %s.", secRecorridos);

				free(secRecorridos);
				tiempoTrans = timeTrans(pedido.sector, pedido.pista, LEER);

				log_info(s_log,"Thread-FileSystem", "Tiempo consumido: %s ms.", tiempoTrans);
				log_info(s_log,"Thread-FileSystem", "Proximo sector: %d:%d.", siguientePedido.pista, siguientePedido.sector);

				break;
			}
*/
			printf("Se escribio el sector exitosamente.\n");
			armarPayload(*pedido, &payload);

			sprintf(msg.payloadID,"%d", pedido->socket_id);
			msg.type = ESCRIBIR_C;
			msg.payloadLength = strlen(payload) + 1;
			msg.payload = payload;

			nipc_send(&msg, pedido->socket_id);
		}
		break;
		case 3: /* CONSOLA */
			tiempoTrans = timeTrans(pedido->sector, pedido->pista, -1);
			if(siguientePedido != NULL)
			{
				sprintf(payload, "%d:%d;%d:%d;%d;%d;%d:%d", pistaActual, sectorActual, pedido->pista, pedido->sector, sectors, tiempoTrans, siguientePedido->pista, siguientePedido->sector);
			}
			else
			{
				sprintf(payload, "%d:%d;%d:%d;%d;%d;%d:%d", pistaActual, sectorActual, pedido->pista, pedido->sector, sectors, tiempoTrans, -1, -1);
			}

			sprintf(msg.payloadID,"%d", pedido->socket_id);
			msg.type = ESCRIBIR_C;
			msg.payloadLength = strlen(payload) + 1;
			msg.payload = payload;
			nipc_send(&msg, pedido->socket_id);
			break;
	}

	free(payload);
}

void armarPayload(struct t_pedido pedido, char **payload){

	switch(modoinit)
	{
		case CONNECT:
			if(pedido.clase == LEER) /* "IdDelPlani;NumeroSector;Buffer;" */
			{
				*payload = (char*) malloc(525);
				memset(*payload, 0, 525);
				sprintf(*payload, "%d;%d;%s;", discoid, pedido.sectorLogico, pedido.buffer);
			}
			if(pedido.clase == ESCRIBIR) /* "IdDelPlani;NumeroSector;" */
			{
				*payload = (char*) malloc(13);
				memset(*payload, 0, 13);
				sprintf(*payload, "%d;%d;", discoid, pedido.sectorLogico);
			}
			break;
		case LISTEN:
			if(pedido.clase == LEER) /* "NumeroSector;Buffer;" */
			{
				*payload = (char*) malloc(525);
				memset(*payload, 0, 525);
				sprintf(*payload, "%d;%s;", pedido.sectorLogico, pedido.buffer);
			}
			if(pedido.clase == ESCRIBIR) /* "NumeroSector;" */
			{
				*payload = (char*) malloc(13);
				memset(*payload, 0, 13);
				sprintf(*payload, "%d;",pedido.sectorLogico);
			}
			break;
	}
}

void agregarPedido(int sector, tipo_clase tipoDePedido, t_proceso proceso, char *buffer, int socket){

	struct t_pedido *pedido;

	pedido = (struct t_pedido*) malloc (sizeof(struct t_pedido));

	pedido->buffer = (char*) malloc( 512 + 1);
	memcpy(pedido->buffer, buffer, strlen(buffer) +1);

	pedido->sectorLogico = sector;
	pedido->pista = getSectorC(sector);
	pedido->sector = getSectorS(sector);
	pedido->clase = tipoDePedido;
	pedido->socket_id = socket;
	pedido->id_proceso = proceso;
	pedido->siguiente = NULL;

	switch(algoritmo)
	{
	case FSCAN:
		if (lista1Ocupada)
		{
			insertarPedido(&lista1, pedido, &cantidadDePedidos1);
		}
		else
		{
			insertarPedido(&lista2, pedido, &cantidadDePedidos2);
		}
		break;
	case SSTF:
		insertarPedido(&lista1, pedido, &cantidadDePedidos1);
		break;
	}
}

int insertarPedido(struct t_pedido **lista, struct t_pedido *pedido, int *cantidadDePedidos){

	struct t_pedido *aux, *ant;
	int 			retval;

	pthread_mutex_lock(&mutexListaPedidos);

	 /* la lista esta vacia */
	if(*lista == NULL)
	{
	   *lista = pedido;
	   ultimoPedido = pedido;
	   (*cantidadDePedidos)++;
	   retval = sem_post(&semPedidos);
	   pthread_mutex_unlock(&mutexListaPedidos);
	   return 0;
	}
	else
	{
		/* la pista del sector a insertar es menor que el primero */
		if(pedido->pista <= (*lista)->pista)
		{
			pedido->siguiente = *lista;
			*lista = pedido;
			(*cantidadDePedidos)++;
			pthread_mutex_unlock(&mutexListaPedidos);
			return 0;
		}

		aux = *lista;
		ant = NULL;

		while((aux != NULL) && (aux->pista < pedido->pista))
		{
			ant = aux;
			aux = aux->siguiente;
		}

		/* si el nuevo nodo va al final */
		if(aux == NULL)
		{
			ant->siguiente = pedido;
			ultimoPedido = pedido;
		}
		else /* si el nuevo nodo va entre ant y aux */
		{
			ant->siguiente = pedido;
			pedido->siguiente = aux;
		}

		(*cantidadDePedidos)++;

		if(algoritmo == SSTF)
			sem_post(&semPedidos);
	}

	pthread_mutex_unlock(&mutexListaPedidos);

	return 0;
}

struct t_pedido* SSTF_proximoPedido(struct t_pedido **lista, struct t_pedido **anterior){

	struct t_pedido	*aux = NULL, *ant = NULL;

	pthread_mutex_lock(&mutexListaPedidos);

	aux = *lista;
	ant = NULL;

	if(aux == NULL)
	{
		return NULL;
	}
	else if(aux->siguiente == NULL)
	{
		return aux;
	}

	while(((aux->siguiente) != NULL) && ((mod((aux->siguiente->pista - pistaActual))) <= (mod((aux->pista - pistaActual)))))
	{
		ant = aux;
		aux = aux->siguiente;
	}

	pthread_mutex_unlock(&mutexListaPedidos);

	*anterior = ant;

	return aux;
}

int recorrerSectores(struct t_pedido pedido, char **secRecorridos)
{
	char 	*secRecorrido;
	int 	i;

	secRecorrido = malloc(sizeof(char*));

	if(sectorActual < pedido.sector)
	{
		for(i = sectorActual; i < pedido.sector; i++)
		{
			sprintf(secRecorrido, "%d:%d, ", pedido.pista, i);
			strcat(*secRecorridos, secRecorrido);
		}
	}
	else
	{
		for(i = sectorActual; i < sectors; i++)
		{
			sprintf(secRecorrido, "%d:%d, ", pedido.pista, i);
			strcat(*secRecorridos, secRecorrido);
		}
		for(i = 0; i < pedido.sector; i++)
		{
			sprintf(secRecorrido, "%d:%d, ", pedido.pista, i);
			strcat(*secRecorridos, secRecorrido);
		}
	}

	free(secRecorrido);
	return 0;
}

int recorrerPendientes(struct t_pedido *lista, int longitud, char **pedidosRecorridos)
{
	char 			*pedidoRecorrido;
	struct t_pedido	*aux;

	pedidoRecorrido = malloc(sizeof(char *));

	aux = lista;

	while(aux != NULL)
	{
		sprintf(pedidoRecorrido, "%d:%d, ", aux->pista, aux->sector);
		strcat(*pedidosRecorridos, pedidoRecorrido);
		aux = aux->siguiente;
	}

	free(pedidoRecorrido);
	return 0;
}

void liberarPedido(struct t_pedido **lista)
{
	struct t_pedido *aux;

	aux = *lista;
	*lista = aux->siguiente;

	free(aux->buffer);

	/* liberamos la memoria */
	free(aux);
}

void sacarPedidoSSTF(struct t_pedido **lista, struct t_pedido *antPedido)
{
	struct t_pedido *aux;

	pthread_mutex_lock(&mutexListaPedidos);

	if(antPedido == NULL)
	{
		aux = *lista;
		*lista = (*lista)->siguiente;
	}
	else
	{
		aux = antPedido->siguiente;
		antPedido->siguiente = antPedido->siguiente->siguiente;
	}

	pthread_mutex_unlock(&mutexListaPedidos);

	free(aux);
}
